package com.hibase.quartz.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageInfo;
import com.hibase.core.mybatis.service.BaseServiceImpl;
import com.hibase.common.exception.HibaseAssert;
import com.hibase.quartz.config.SingleScheduler;
import com.hibase.quartz.config.job.BaseJob;
import com.hibase.quartz.constant.QuartzType;
import com.hibase.quartz.entity.MdsJob;
import com.hibase.quartz.entity.MdsRedisJob;
import com.hibase.quartz.entity.vo.MdsImmediateJob;
import com.hibase.quartz.entity.vo.MdsJobSearchVO;
import com.hibase.quartz.mapper.MdsJobMapper;
import com.hibase.quartz.service.MdsJobService;
import com.hibase.quartz.utils.MdsJobUtils;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author LYL
 * @since 2020-11-18
 */
@Service
@Slf4j
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class MdsJobServiceImpl extends BaseServiceImpl<MdsJobMapper, MdsJob> implements MdsJobService {

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    @Autowired
    private SingleScheduler singleScheduler;

    @Value("${hibase.MdsJob.machineName:default}")
    public String machineName;

    @Autowired
    private RedisTemplate<String,String> stringRedisTemplate;


    @Autowired
    private BaseJob baseJob;

    /**
     * 查询列表
     */
    @Override
    public PageInfo<MdsJob> selectMdsJobList(MdsJobSearchVO searchVO) {
        QueryWrapper<MdsJob> mdsJobQueryWrapper = new QueryWrapper<>();

        if (StrUtil.isNotBlank(searchVO.getName())) {
            mdsJobQueryWrapper.eq("name", searchVO.getName());
            mdsJobQueryWrapper.or().eq("bean_name", searchVO.getName());
        }
        return super.page(searchVO, mdsJobQueryWrapper);
    }

    /**
     * 改变记录
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveMdsJob(MdsJob entity) {

        //查询加载该任务

        try {

            Scheduler singleSchedulerInstance = singleScheduler.getInstance();

            JobKey jobKey = MdsJobUtils.getJobKey(entity);

            Scheduler scheduler = schedulerFactoryBean.getScheduler();

            scheduler.pauseJob(jobKey);

            scheduler.unscheduleJob(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));

            scheduler.deleteJob(jobKey);

            singleSchedulerInstance.pauseJob(jobKey);

            singleSchedulerInstance.unscheduleJob(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));

            singleSchedulerInstance.deleteJob(jobKey);

            JobDataMap map = MdsJobUtils.getJobDataMap(entity);

            JobDetail jobDetail = MdsJobUtils.getJobDetail(jobKey, entity, map);

            if (entity.getStatus().equals("OPEN")) {

                //判断当前job的执行区域，是否为当前机器
                String executionArea = jobDetail.getJobDataMap().getString("executionArea");

                if (StrUtil.isBlank(executionArea)) {

                    //如果 executionArea为空则代表进入集群调度器
                    scheduler.scheduleJob(jobDetail, MdsJobUtils.getTrigger(entity));
                    log.info("Job executionArea为空，进入集群调度器 : {} ", entity.getName());

                } else if (machineName.equals(executionArea)) {

                    //如果限定为本机器执行，则进入独立调度器
                    singleSchedulerInstance.scheduleJob(jobDetail, MdsJobUtils.getTrigger(entity));
                    log.info("Job : {} 限定为本机器执行，进入独立调度器 :{}", entity.getName(),singleSchedulerInstance.getSchedulerName());

                } else {

                    //限定执行区域，不是本机器，不做操作
                    log.info("Job 限定执行区域，不是本机器，不进调度器 : {} ", entity.getName());


                }

            } else {

                log.info("Job jump name : {} , Because {} status is {}", entity.getName(), entity.getName(), entity.getStatus());

            }

        } catch (Exception e) {

            log.error(e.getMessage());

            throw new RuntimeException(e.getMessage());

        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public MdsJob saveJobEntity(MdsJob entity) {

        if (StrUtil.isBlank(entity.getTimeZone())) {

            entity.setTimeZone("UTC+8");

        }

        HibaseAssert.notNull(entity.getCron(), "cron不能为空!");

        HibaseAssert.isTrue(CronExpression.isValidExpression(entity.getCron()), "cron 无效!");

        entity.setCron(MdsJobUtils.timeZoneHandler(entity.getCron(), entity.getTimeZone()));

        return super.store(entity);
    }

    /**
     * 查询详情
     */
    @Override
    public MdsJob viewMdsJob(String id) {

        MdsJob entity = super.getById(id, true);

        return entity;
    }

    /**
     * 删除记录
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMdsJob(String id) {

        MdsJob mdsJob = this.getOne(new QueryWrapper<MdsJob>().eq("id", id));

        Scheduler singleSchedulerInstance = singleScheduler.getInstance();

        JobKey jobKey = MdsJobUtils.getJobKey(mdsJob);

        Scheduler scheduler = schedulerFactoryBean.getScheduler();

        try {

            scheduler.pauseJob(jobKey);

            scheduler.unscheduleJob(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));

            scheduler.deleteJob(jobKey);

            singleSchedulerInstance.pauseJob(jobKey);

            singleSchedulerInstance.unscheduleJob(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));

            singleSchedulerInstance.deleteJob(jobKey);

            super.deleteBy(id);

        } catch (Exception e) {

            log.error(e.getMessage());

        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMdsJob(List<String> ids) {
        ids.forEach(this::deleteMdsJob);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reStartAllJobs() throws SchedulerException {

        Scheduler singleSchedulerInstance = singleScheduler.getInstance();

        Scheduler scheduler = schedulerFactoryBean.getScheduler();

        Set<JobKey> JobKeys = scheduler.getJobKeys(GroupMatcher.anyGroup());

        Set<JobKey> singleJobKeys = singleSchedulerInstance.getJobKeys(GroupMatcher.anyGroup());

        //暂停所有JOB
        scheduler.pauseJobs(GroupMatcher.anyGroup());

        singleSchedulerInstance.pauseJobs(GroupMatcher.anyGroup());

        //删除从数据库中注册的所有JOB
        for (JobKey jobKey : JobKeys) {

            scheduler.unscheduleJob(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));

            scheduler.deleteJob(jobKey);

        }

        //删除单例调度器job
        for (JobKey jobKey : singleJobKeys) {

            singleSchedulerInstance.unscheduleJob(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));

            singleSchedulerInstance.deleteJob(jobKey);

        }

        //从数据库中注册的所有JOB
        for (MdsJob job : this.list(new QueryWrapper<MdsJob>().eq("status", "OPEN"))) {

            log.info("Job register name : {} , group : {} , cron : {}", job.getName(), job.getJobGroup(), job.getCron());

            JobDataMap map = MdsJobUtils.getJobDataMap(job);

            JobKey jobKey = MdsJobUtils.getJobKey(job);

            JobDetail jobDetail = MdsJobUtils.getJobDetail(jobKey, job, map);

            //判断当前job的执行区域，是否为当前机器
            String executionArea = jobDetail.getJobDataMap().getString("executionArea");

            if (StrUtil.isBlank(executionArea)) {

                //如果 executionArea为空则代表进入集群调度器
                scheduler.scheduleJob(jobDetail, MdsJobUtils.getTrigger(job));

            } else if (machineName.equals(executionArea)) {

                //如果限定为本机器执行，则进入独立调度器
                singleSchedulerInstance.scheduleJob(jobDetail, MdsJobUtils.getTrigger(job));

            } else {

                //限定执行区域，不是本机器，不做操作

            }

        }

    }

    @Override
    public void immediateJob(MdsImmediateJob mdsImmediateJob) {

        MdsJob mdsJob = this.getById(mdsImmediateJob.getId());

        mdsJob.setExecutionArea(StrUtil.isBlank(mdsJob.getExecutionArea())?machineName:mdsJob.getExecutionArea());

        mdsJob.setParameter(mdsImmediateJob.getParameter());


        MdsRedisJob redisJob = new MdsRedisJob(){{

            setType(QuartzType.IMMEDIATEJOB.getKey());

            setMdsJob(mdsJob);

        }};

        stringRedisTemplate.convertAndSend("SingleClusterQuartz",JSONUtil.toJsonStr(redisJob));


    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void runJob(MdsJob mdsJob) {

        //判断该任务是否有当前机器执行
        if(!machineName.equals(mdsJob.getExecutionArea())){
            return;
        }

        //直接调用通用方法执行
        baseJob.process(mdsJob);


    }

}
